package org.Yiran.timetale_re.menu;

import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.network.PacketDistributor;
import org.Yiran.timetale_re.item.items.RuneBlueprintBookItem;
import org.Yiran.timetale_re.menu.slot.RuneBlueprintOnlySlot;
import org.Yiran.timetale_re.network.NetworkChannel;
import org.Yiran.timetale_re.network.SCResponsePacket;
import org.jetbrains.annotations.NotNull;

/**
 * 符文图纸手册菜单类
 * <p>
 * 该类继承自AbstractContainerMenu，用于管理符文图纸手册的GUI界面。
 * 提供27个槽位用于存储符文图纸，以及玩家物品栏和快捷栏的显示。
 * 支持物品的存取、NBT数据的保存与同步等功能。
 * </p>
 */
public class RuneBlueprintBookMenu extends AbstractContainerMenu {
    
    // ==============================
    // 常量定义
    // ==============================

    /** 手册槽位数量 */
    public static final int BOOK_SLOTS = 27;
    /** 玩家物品栏行数 */
    private static final int PLAYER_INVENTORY_ROWS = 3;
    /** 玩家物品栏列数 */
    private static final int PLAYER_INVENTORY_COLS = 9;
    /** 玩家快捷栏槽数量 */
    private static final int PLAYER_HOTBAR_SLOTS = 9;

    // ==============================
    // 实例变量
    // ==============================

    /** 符文图纸手册物品堆 */
    private final ItemStack bookStack;
    /** 手册物品栏处理器 */
    private final ItemStackHandler bookInventory;

    // ==============================
    // 构造方法
    // ==============================

    /**
     * 构造函数，创建符文图纸手册菜单
     * <p>
     * 初始化菜单，设置手册物品堆，创建物品栏处理器，
     * 加载NBT数据，添加各种槽位并同步数据。
     * </p>
     *
     * @param windowId        窗口ID
     * @param playerInventory 玩家物品栏
     * @param bookStack       符文图纸手册物品堆
     */
    public RuneBlueprintBookMenu(int windowId, Inventory playerInventory, ItemStack bookStack) {
        super(MenuRegister.RUNE_BLUEPRINT_BOOK_MENU.get(), windowId);
        this.bookStack = bookStack;

        // 初始化物品栏处理器
        this.bookInventory = createBookInventoryHandler();

        // 从NBT加载物品
        loadBookInventory();

        // 添加各种槽位
        addBookSlots();
        addPlayerInventorySlots(playerInventory);
        addPlayerHotbarSlots(playerInventory);
        syncBookToItemStack();
    }

    /**
     * 从网络数据创建菜单
     * <p>
     * 通过网络数据缓冲区读取物品堆信息来创建菜单实例。
     * </p>
     *
     * @param windowId        窗口ID
     * @param playerInventory 玩家物品栏
     * @param data            网络数据缓冲区
     */
    public RuneBlueprintBookMenu(int windowId, Inventory playerInventory, FriendlyByteBuf data) {
        this(windowId, playerInventory, data.readItem());
    }

    // ==============================
    // 物品栏处理器方法
    // ==============================

    /**
     * 创建手册物品栏处理器
     * <p>
     * 创建具有27个槽位的物品栏处理器，并重写isItemValid方法
     * 以限制只能放置符文图纸类型的物品。
     * </p>
     *
     * @return 物品栏处理器实例
     */
    private ItemStackHandler createBookInventoryHandler() {
        return new ItemStackHandler(BOOK_SLOTS) {
            @Override
            public boolean isItemValid(int slot, @NotNull ItemStack stack) {
                return isAllowedInBook(slot, stack);
            }
        };
    }

    /**
     * 检查物品是否允许放入手册槽位
     * <p>
     * 验证指定物品是否为符文图纸，只有符文图纸才能放入手册槽位。
     * </p>
     *
     * @param slot  槽位索引
     * @param stack 物品堆
     * @return 如果允许放入返回true，否则返回false
     */
    private boolean isAllowedInBook(int slot, ItemStack stack) {
        return RuneBlueprintBookItem.isRuneBlueprint(stack);
    }

    // ==============================
    // 槽位添加方法
    // ==============================

    /**
     * 添加手册槽位 (3x9网格)
     * <p>
     * 添加27个手册槽位，排列为3行9列的网格布局。
     * </p>
     */
    private void addBookSlots() {
        for (int i = 0; i < BOOK_SLOTS; i++) {
            addSingleBookSlot(i);
        }
    }

    /**
     * 添加单个手册槽位
     * <p>
     * 创建并添加一个符文图纸专用槽位到菜单中。
     * </p>
     *
     * @param slotIndex 槽位索引
     */
    private void addSingleBookSlot(int slotIndex) {
        int x = calculateBookSlotX(slotIndex);
        int y = calculateBookSlotY(slotIndex);
        this.addSlot(new RuneBlueprintOnlySlot(bookInventory, slotIndex, x, y));
    }

    /**
     * 计算手册槽位X坐标
     * <p>
     * 根据槽位索引计算其在GUI中的X坐标位置。
     * </p>
     *
     * @param slotIndex 槽位索引
     * @return X坐标
     */
    private int calculateBookSlotX(int slotIndex) {
        return 8 + (slotIndex % 9) * 18;
    }

    /**
     * 计算手册槽位Y坐标
     * <p>
     * 根据槽位索引计算其在GUI中的Y坐标位置。
     * </p>
     *
     * @param slotIndex 槽位索引
     * @return Y坐标
     */
    private int calculateBookSlotY(int slotIndex) {
        return 17 + (slotIndex / 9) * 18;
    }

    /**
     * 添加玩家物品栏槽位
     * <p>
     * 添加玩家物品栏的槽位，排列为3行9列的网格布局。
     * </p>
     *
     * @param playerInventory 玩家物品栏
     */
    private void addPlayerInventorySlots(Inventory playerInventory) {
        for (int row = 0; row < PLAYER_INVENTORY_ROWS; row++) {
            for (int col = 0; col < PLAYER_INVENTORY_COLS; col++) {
                addSinglePlayerInventorySlot(playerInventory, row, col);
            }
        }
    }

    /**
     * 添加单个玩家物品栏槽位
     * <p>
     * 创建并添加一个普通槽位到菜单中，用于显示玩家物品栏中的物品。
     * </p>
     *
     * @param playerInventory 玩家物品栏
     * @param row             行号
     * @param col             列号
     */
    private void addSinglePlayerInventorySlot(Inventory playerInventory, int row, int col) {
        int slotIndex = col + row * PLAYER_INVENTORY_COLS + PLAYER_INVENTORY_COLS;
        int x = 8 + col * 18;
        int y = 84 + row * 18;
        this.addSlot(new Slot(playerInventory, slotIndex, x, y));
    }

    /**
     * 添加玩家快捷栏槽位
     * <p>
     * 添加玩家快捷栏的槽位，排列为1行9列的布局。
     * </p>
     *
     * @param playerInventory 玩家物品栏
     */
    private void addPlayerHotbarSlots(Inventory playerInventory) {
        for (int col = 0; col < PLAYER_HOTBAR_SLOTS; col++) {
            addSingleHotbarSlot(playerInventory, col);
        }
    }

    /**
     * 添加单个快捷栏槽位
     * <p>
     * 创建并添加一个普通槽位到菜单中，用于显示玩家快捷栏中的物品。
     * </p>
     *
     * @param playerInventory 玩家物品栏
     * @param col             列号
     */
    private void addSingleHotbarSlot(Inventory playerInventory, int col) {
        int x = 8 + col * 18;
        int y = 142;
        this.addSlot(new Slot(playerInventory, col, x, y));
    }

    // ==============================
    // 物品移动方法
    // ==============================

    /**
     * 快速移动物品堆
     * <p>
     * 处理玩家Shift+点击或双击物品时的快速移动逻辑。
     * 支持手册槽位与玩家物品栏之间的物品移动。
     * </p>
     *
     * @param player 玩家对象
     * @param index  槽位索引
     * @return 移动后的物品堆
     */
    @Override
    public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) {
        Slot slot = this.slots.get(index);
        if (!slot.hasItem()) {
            return ItemStack.EMPTY;
        }

        ItemStack originalStack = slot.getItem();
        ItemStack stackCopy = originalStack.copy();

        // 如果点击的是手册槽位(0-26)，移动到玩家物品栏
        if (index >= 0 && index < BOOK_SLOTS) {
            // 尝试移动到玩家物品栏和快捷栏(27-62)
            if (!moveItemStackTo(stackCopy, BOOK_SLOTS, this.slots.size(), true)) {
                return ItemStack.EMPTY;
            }
        }
        // 如果点击的是玩家物品栏或快捷栏，且物品是符文图纸
        else if (RuneBlueprintBookItem.isRuneBlueprint(originalStack)) {
            // 特殊处理符文图纸的移动
            ItemStack toMove = originalStack.copy();
            toMove.setCount(1); // 只移动一个

            // 尝试找到一个空槽位放置
            boolean placed = false;
            for (int i = 0; i < BOOK_SLOTS; i++) {
                Slot targetSlot = this.slots.get(i);
                if (!targetSlot.hasItem() && targetSlot.mayPlace(toMove)) {
                    targetSlot.set(toMove);
                    originalStack.shrink(1); // 减少原堆叠数量
                    placed = true;
                    break;
                }
            }

            if (!placed) {
                return ItemStack.EMPTY;
            }
        }

        // 如果不是符文图纸，不允许移动
        else {
            return ItemStack.EMPTY;
        }

        if (stackCopy.isEmpty()) {
            slot.set(ItemStack.EMPTY);
        } else {
            slot.setChanged();
        }

        slot.onTake(player, stackCopy);
        saveBookInventory();
        return originalStack;
    }

    // ==============================
    // 数据加载与保存方法
    // ==============================

    /**
     * 从NBT加载手册物品栏
     * <p>
     * 从手册物品堆的NBT数据中加载存储的物品信息。
     * </p>
     */
    private void loadBookInventory() {
        if (hasBookInventoryData()) {
            bookInventory.deserializeNBT(getBookInventoryTag());
        }
    }

    /**
     * 检查是否有手册物品栏数据
     * <p>
     * 检查手册物品堆是否包含物品栏数据的NBT标签。
     * </p>
     *
     * @return 如果有数据返回true，否则返回false
     */
    private boolean hasBookInventoryData() {
        if (bookStack.getTag() != null) {
            return bookStack.hasTag() && bookStack.getTag().contains("BookInventory");
        }
        return false;
    }

    /**
     * 获取手册物品栏NBT标签
     * <p>
     * 从手册物品堆中获取物品栏数据的NBT复合标签。
     * </p>
     *
     * @return NBT复合标签
     */
    private net.minecraft.nbt.CompoundTag getBookInventoryTag() {
        if (bookStack.getTag() != null) {
            return bookStack.getTag().getCompound("BookInventory");
        }
        return null;
    }

    /**
     * 将物品栏保存到NBT
     * <p>
     * 将当前物品栏状态保存到手册物品堆的NBT数据中，
     * 并同步到客户端。
     * </p>
     */
    private void saveBookInventory() {
        bookStack.getOrCreateTag().put("BookInventory", bookInventory.serializeNBT());

        // 同步到客户端
        syncToClient();
    }

    /**
     * 同步数据到客户端
     * <p>
     * 通过网络将手册物品堆数据发送到客户端，保持数据同步。
     * </p>
     */
    private void syncToClient() {
        Player player = getPlayer();
        if (player instanceof ServerPlayer serverPlayer) {
            NetworkChannel.CHANNEL.send(
                    PacketDistributor.PLAYER.with(() -> serverPlayer),
                    new SCResponsePacket.RuneBlueprintBookSyncPacket(bookStack)
            );
        }
    }

    // ==============================
    // 菜单验证与关闭方法
    // ==============================

    /**
     * 检查菜单是否仍然有效
     * <p>
     * 验证菜单是否可以继续使用，包括手册有效性、玩家存活状态和手册持有情况。
     * </p>
     *
     * @param player 玩家对象
     * @return 如果菜单有效返回true，否则返回false
     */
    @Override
    public boolean stillValid(@NotNull Player player) {
        return isBookValid() && isPlayerAlive(player) && isBookInHands(player);
    }

    /**
     * 检查手册是否有效
     * <p>
     * 验证手册物品堆是否非空。
     * </p>
     *
     * @return 如果有效返回true，否则返回false
     */
    private boolean isBookValid() {
        return !bookStack.isEmpty();
    }

    /**
     * 检查玩家是否存活
     * <p>
     * 验证玩家是否处于存活状态。
     * </p>
     *
     * @param player 玩家
     * @return 如果存活返回true，否则返回false
     */
    private boolean isPlayerAlive(Player player) {
        return player.isAlive();
    }

    /**
     * 检查手册是否在玩家手中
     * <p>
     * 验证手册物品堆是否在玩家的主手或副手中。
     * </p>
     *
     * @param player 玩家
     * @return 如果在手中返回true，否则返回false
     */
    private boolean isBookInHands(Player player) {
        return (player.getMainHandItem() == bookStack || player.getOffhandItem() == bookStack);
    }

    /**
     * 菜单关闭时的处理方法
     * <p>
     * 当玩家关闭菜单时调用，保存物品栏数据。
     * </p>
     *
     * @param player 玩家对象
     */
    @Override
    public void removed(@NotNull Player player) {
        super.removed(player);
        saveBookInventory();
    }

    // ==============================
    // 辅助方法
    // ==============================

    /**
     * 获取当前打开菜单的玩家
     * <p>
     * 通过遍历槽位找到与玩家物品栏关联的玩家对象。
     * </p>
     *
     * @return 玩家对象
     */
    private Player getPlayer() {
        for (Slot slot : slots) {
            if (slot.container instanceof Inventory) {
                return ((Inventory) slot.container).player;
            }
        }
        return null;
    }

    /**
     * 同步图册数据到物品堆
     * <p>
     * 将当前物品栏状态强制保存到手册物品堆中。
     * </p>
     */
    private void syncBookToItemStack() {
        // 强制将当前bookInventory状态保存到bookStack
        bookStack.getOrCreateTag().put("BookInventory", bookInventory.serializeNBT());
    }
}